package com.hup.sidebar.view.touch;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.WindowManager;
import android.widget.FrameLayout;

import androidx.annotation.NonNull;

import com.hup.sidebar.model.AppConfig;

import java.util.Collections;
import java.util.List;

import lombok.Getter;

/**
 * 激活器
 *
 * @author hugan
 * @date 2022/6/3
 */
@SuppressLint("ViewConstructor")
public class ActivationView extends FrameLayout {
    private static final String TAG = ActivationView.class.getSimpleName();
    private static final int BACKGROUND_COLOR = Color.GREEN;

    private final WindowManager windowManager;
    private final AppConfig appConfig;
    @Getter
    private final PieView pieView;
    private MyMotionEvent lastEvent = null;
    private final Runnable showPieViewAction;

    /**
     * @param gravity {@link Gravity#START}, {@link Gravity#END},
     */
    public ActivationView(@NonNull Context context, WindowManager windowManager, int gravity, AppConfig appConfig) {
        super(context);
        this.windowManager = windowManager;
        this.appConfig = appConfig;
        setWillNotDraw(true);
        setEnabled(false);
        pieView = new PieView(context, gravity, appConfig);
        pieView.setVisibility(GONE);
        this.addView(pieView);
        showPieViewAction = () -> pieView.setVisibility(VISIBLE);
    }

    /**
     * @param e0 注意,这个对象是[按下,滑动等]复用的! post时传参时,要'克隆'
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent e0) {
        /*
        无法准确收到[触摸事件]
        - 开启[手势导航,侧滑返回]时, 无法收到任何事件 //推测[侧滑功能图层]拦截了全部事件
        - 即使关闭[侧滑返回],也无法准确收到事件
            - 无法实现[按下事件 马上显示浮窗]
            - 只能收到:"按下并拖拽,快速点击"
            - 不能收到:"长按时的按下事件,"
        - 使用鼠标时:能准确识别按下事件/马上有按下事件/马上弹出菜单,(如果有边缘返回功能,则按下后快速滑动时会收到cancel事件)
        - 不同[厂商,安卓版本,系统版本]会有差异
        - flyme9后: 不能开启[侧滑返回]
        - flyme12: 可以开启[侧滑返回],且[侧滑功能]正常,但仍然没有[按下事件] //推测[侧滑图层]的z-index降低了
         */
        //Log.i(TAG, "dispatchTouchEvent: " + ThreadLocalRandom.current().nextInt() + e);

        //过滤事件
        MyMotionEvent e = new MyMotionEvent(e0);
        if (e.equals(lastEvent)) return true;
        lastEvent = e;
        //Log.i(TAG, "dispatchTouchEvent: " + e);
        if (e.getToolType() == MotionEvent.TOOL_TYPE_UNKNOWN) return false;

        int action = e.getActionMasked();
        if (action == MotionEvent.ACTION_DOWN) {
            onTouch(true);
        } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
            //cancel:如被flyme的系统悬浮窗/其他app拦截了事件
            onTouch(false);
        }

        pieView.onMyTouchEvent(e);
        return true;
    }

    /**
     * 处理触摸事件
     *
     * @param isDown true-铺满屏幕,false-缩小回配置尺寸;显示/隐藏pie
     * @apiNote [绘制残留/过渡帧]
     * - 问题: 修正前, [按下,抬起]时, 概率性出现n帧[pieView错位画面]
     * - 原因: 实际上pieView没有错位,只是[激活器]updateViewLayout改变了位置和尺寸
     * - 处理: 要按序且配合post 调用[updateViewLayout和visible]
     * - 观察调试: 激活器尺寸拉到最大,靠上显示, 注释pieView.setVisibility(GONE)
     */
    private void onTouch(boolean isDown) {
        //Log.i(TAG, "dispatchTouchEvent: " + isDown);
        WindowManager.LayoutParams params = (WindowManager.LayoutParams) getLayoutParams();
        if (isDown) {
            params.width = LayoutParams.MATCH_PARENT;
            params.height = LayoutParams.MATCH_PARENT;
        } else {
            params.width = appConfig.activeWidth;
            params.height = appConfig.activeHeight;
        }
        //实现:触摸一次后总会变透明
        params.format = PixelFormat.TRANSPARENT;
        if (Build.VERSION.SDK_INT >= 31) params.alpha = 1.0f;
        setBackground(null);//测试:注释时, 始终显示背景色

        //处理pie
        if (isDown) {
            removeCallbacks(showPieViewAction);
            windowManager.updateViewLayout(this, params);
            post(showPieViewAction);
        } else {
            removeCallbacks(showPieViewAction);
            pieView.onRemoveFromScreen();
            pieView.setVisibility(GONE);//注释这行,可观察[过渡帧]
            post(() -> windowManager.updateViewLayout(this, params));
        }
    }

    /**
     * 触发场景:首次初始化, 按下/抬起事件
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldW, int oldH) {
        super.onSizeChanged(w, h, oldW, oldH);
        Log.d(TAG, "onSizeChanged new:" + w + "," + h + " old:" + oldW + "," + oldH);

        if (w > 0 && h > 0) {
            if (VERSION.SDK_INT >= VERSION_CODES.Q) {
                /*
                目的:屏蔽系统的[两侧返回]功能
                https://developer.android.google.cn/training/gestures/gesturenav
                https://cloud.tencent.com/developer/article/1555442
                魅族坑:
                    无效! SeekBar也无效!

                官网demo是放在 onLayout,但有异味警告
                    Avoid object allocations during draw/layout operations
                 */
                Rect boundingBox = new Rect(getLeft(), getTop(), getRight(), getBottom());
                List<Rect> exclusions = Collections.singletonList(boundingBox);
                setSystemGestureExclusionRects(exclusions);
            }
        }

    }

    public void addToScreen() {
        WindowManager.LayoutParams params = new WindowManager.LayoutParams();
        params.width = appConfig.activeWidth;
        params.height = appConfig.activeHeight;
        params.gravity = pieView.getGravity();
        params.y = appConfig.activeYOffset;
        params.type = VERSION.SDK_INT >= VERSION_CODES.O
                ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
        params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        params.format = PixelFormat.TRANSPARENT;//创建时透明

        WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        windowManager.addView(this, params);
    }

    public void removeFromScreen() {
        windowManager.removeView(this);
    }

    @SuppressWarnings("unused")
    public boolean isVisible() {
        WindowManager.LayoutParams params = (WindowManager.LayoutParams) getLayoutParams();
        return params.format == PixelFormat.OPAQUE;
    }

    /**
     * @param b true-显示成黑色
     */
    public void setVisible(boolean b) {
        WindowManager.LayoutParams params = (WindowManager.LayoutParams) getLayoutParams();
        params.format = b ? PixelFormat.OPAQUE : PixelFormat.TRANSPARENT;
        windowManager.updateViewLayout(this, params);
        if (b) setBackgroundColor(BACKGROUND_COLOR);
        else setBackground(null);
    }

    public void onActivationConfigUpdated() {
        //Log.i(TAG, "onActivationConfigUpdated: ");
        WindowManager.LayoutParams params = (WindowManager.LayoutParams) getLayoutParams();
        params.width = appConfig.activeWidth;
        params.height = appConfig.activeHeight;
        params.y = appConfig.activeYOffset;
        params.format = PixelFormat.OPAQUE;
        windowManager.updateViewLayout(this, params);
        setBackgroundColor(BACKGROUND_COLOR);
    }

    /**
     * 切换横竖屏时,让悬浮窗始终体紧边缘
     * - 部分设备横屏时,[竖屏顶部状态栏一侧]是始终无法体紧边缘的
     * - 不添加[onConfigurationChanged监听]时, 悬浮窗也可以自动旋转,但部分设备[不能自动贴紧边缘],所以需要触发本方法
     */
    public void onOrientationChanged() {
        WindowManager.LayoutParams params = (WindowManager.LayoutParams) getLayoutParams();
        windowManager.updateViewLayout(this, params);
    }

}
